<template>
  <div class="page-suno flex-h">
    <div class="left-bar">
      <!-- <div class="bar-top">
        <el-tooltip effect="light" content="定义模式" placement="top">
          <black-switch v-model:value="custom" size="large" />
        </el-tooltip>
        <black-select
          v-model:value="data.model"
          :options="models"
          placeholder="请选择模型"
          style="width: 100px"
        />
      </div> -->

      <div class="params">
        <div class="pure-music flex-h flex-hv-center">
          <span class="switch">
            <el-switch
              v-model="data.makeInstrumental"
              size="default"
              @change="data.prompt = ''"
              class="deafult_style"
            />
          </span>
          <span class="text ml-10">纯音乐</span>
        </div>
        <div v-if="custom">
          <div class="item-group">
            <div class="label flex-h flex-vh-center">
              <span class="text">歌曲名称(必填)</span>
              <el-popover
                placement="right"
                :width="200"
                trigger="hover"
                content="给你的歌曲起一个标题，以便于分享、发现和组织。"
              >
                <template #reference>
                  <el-icon class="iconTip">
                    <InfoFilled />
                  </el-icon>
                </template>
              </el-popover>
            </div>
            <div class="item posr">
              <black-input
                v-model:value="data.title"
                :maxlength="20"
                type="textarea"
                :rows="5"
                placeholder="请输入歌曲名称..."
              />
            </div>
          </div>
          <div class="item-group" v-if="!data.makeInstrumental">
            <div class="label flex-h flex-hv-center">
              <div class="flex-1">
                <span class="text">歌词(必填)</span>
                <el-popover
                  placement="right"
                  :width="200"
                  trigger="hover"
                  content="自己写歌词或寻求 AI 的帮助。使用两节歌词（8 行）可获得最佳效果。"
                >
                  <template #reference>
                    <el-icon class="iconTip">
                      <InfoFilled />
                    </el-icon>
                  </template>
                </el-popover>
              </div>
              <span class="empty-lyrics_btn" @click="data.prompt = ''">清空</span>
            </div>
            <div class="item posr" element-loading-text="正在生成歌词...">
              <black-input
                v-model:value="data.prompt"
                type="textarea"
                :rows="10"
                placeholder="请在这里输入你自己写的歌词..."
              />
              <el-button
                type="primary"
                plain
                class="gen-lyrics_btn"
                :loading="lyricLoading"
                size="small"
                @click="createLyrics({ prompt: data.title })"
              >
                <div class="flex-h flex-center">
                  <i class="iconfont icon-zhinengshengcheng mr-10"></i>AI生成歌词（{{
                    lyricPower
                  }}算力）
                </div>
              </el-button>
              <!-- <button class="btn btn-lyric" @click="createLyric">
                生成歌词
              </button> -->
            </div>
          </div>

          <div class="item-group">
            <div class="label flex-h flex-vh-center">
              <span class="text">音乐风格</span>
              <el-popover
                placement="right"
                :width="200"
                trigger="hover"
                content="描述您想要的音乐风格（例如“原声流行音乐”）"
              >
                <template #reference>
                  <el-icon class="iconTip">
                    <InfoFilled />
                  </el-icon>
                </template>
              </el-popover>
            </div>
            <div class="item">
              <black-input
                v-model:value="data.tags"
                type="textarea"
                :maxlength="120"
                :rows="5"
                placeholder="请输入音乐风格，多个风格之间用英文逗号隔开..."
              />
            </div>

            <div class="tag-select mb-5">
              <div class="inner">
                <span class="tag" @click="selectTag(tag)" v-for="tag in tags" :key="tag.value">{{
                  tag.label
                }}</span>
              </div>
            </div>
          </div>
        </div>

        <div v-else>
          <div class="left-item">
            <div class="label">
              <span class="text">歌曲描述</span>
              <el-popover
                placement="right"
                :width="200"
                trigger="hover"
                content="描述您想要的音乐风格和主题（例如：关于假期的流行音乐）。请使用流派和氛围，而不是特定的艺术家和歌曲风格，AI无法识别。"
              >
                <template #reference>
                  <el-icon>
                    <InfoFilled />
                  </el-icon>
                </template>
              </el-popover>
            </div>
            <div class="item">
              <black-input
                v-model:value="data.prompt"
                type="textarea"
                :rows="10"
                placeholder="例如：一首关于鸟人的摇滚歌曲..."
              />
            </div>
          </div>
        </div>
        <!-- 
        <div class="ref-song" v-if="refSong">
          <div class="label">
            <span class="text">续写</span>
            <el-popover
              placement="right"
              :width="200"
              trigger="hover"
              content="输入额外的歌词，根据您之前的歌词来扩展歌曲。"
            >
              <template #reference>
                <el-icon>
                  <InfoFilled />
                </el-icon>
              </template>
            </el-popover>
          </div>

          <div class="item">
            <div class="song">
              <el-image :src="refSong.cover_url" fit="cover" />
              <span class="title">{{ refSong.title }}</span>
              <el-button
                type="info"
                @click="removeRefSong"
                size="small"
                :icon="Delete"
                circle
              />
            </div>
            <div class="extend-secs">
              从 <input v-model="refSong.extend_secs" type="text" /> 秒开始续写
            </div>
          </div>
        </div> -->

        <div class="item">
          <!-- <el-tooltip :visible="tooltipVisivle && !params.prompt">
            <template #content>
              <span>请输入创意描述</span>
            </template> -->
          <!-- :disabled="!params.prompt"
             @mouseenter="tooltipVisivle = true"
             @mouseleave="tooltipVisivle = false" -->
          <el-button
            class="liner-btn liner-btn-opacity"
            @click="create"
            :disabled="isGenStatus"
            v-loading="musciLoading"
            round
          >
            <!-- <img src="/images/create-new.svg" alt="" /> -->
            {{ btnText }}
          </el-button>
          <!-- </el-tooltip> -->
        </div>
      </div>
    </div>
    <!-- flex-v -->
    <div class="middle-box flex-1 flex-v flex-center">
      <template v-if="showPlayer && list[songIndex] && curPlay">
        <ul class="flex-1 middle-box__item flex-h flex-vv-center">
          <div class="middle-box__item_container flex-h">
            <li class="middle-box_imgbox flex-v flex-center">
              <el-image :src="list[songIndex].imageLargeUrl" fit="cover"></el-image>
              <div class="middle-box_imgbox_func mt-50 flex-h flex-center">
                <a
                  :href="list[songIndex].videoUrl"
                  :download="list[songIndex].title + '.mp3'"
                  target="_blank"
                >
                  <button class="flex-h flex-center">
                    <!-- <el-icon class="ft-20 mr-5"> <Download /> </el-icon> -->
                    下载音乐
                  </button>
                </a>
                <button class="flex-h flex-center" @click="removeJob(list[songIndex])">
                  <!-- <el-icon class="ft-20 mr-5"> <Delete /> </el-icon> -->
                  删除音乐
                </button>
              </div>
            </li>
            <li class="middle-box_imgText h100 flex-v">
              <div class="title">
                <h2>{{ list[songIndex].title }}</h2>
              </div>
              <div class="tags mb-15">{{ list[songIndex].tags }}</div>
              <!-- <p class="flex-v">{{ curPlay.prompt }}</p> -->
              <el-scrollbar
                :wrap-style="{
                  width: '200px',
                  height: '500px',
                }"
              >
                <!-- <black-input
               :value="list[songIndex].prompt"
                type="textarea"
                :rows="10"
              /> -->
                <div style="width: 100%">
                  <div
                    style="
                      word-break: break-all;
                      line-height: 1.6;
                      white-space: pre-wrap;
                      width: 100%;
                    "
                  >
                    {{ list[songIndex].prompt }}
                  </div>
                </div>
              </el-scrollbar>
            </li>
          </div>
        </ul>
        <div class="music-player">
          <music-player
            :songs="list"
            :value="songIndex"
            @update:modelValue="($event) => (songIndex = $event)"
            ref="playerRef"
            :show-close="false"
            @close="showPlayer = false"
          />
        </div>
      </template>
      <!-- <el-empty
        v-else
        image="/images/goose-bath.gif"
        :image-size="250"
        description="快去生成你的创意吧～"
      /> -->
      <Empty v-else image="/images/goose-bath.gif" description="只要有音乐，就不会有世界末日。" />
    </div>
    <!-- v-loading="loading" -->
    <div class="right-box" element-loading-background="rgba(100,100,100,0.3)">
      <div class="list-box flex-v">
        <h3 class="ml-20 ft-16">历史</h3>
        <div
          class="waterfall-box flex-1"
          v-if="list.length"
          v-infinite-scroll="getMusicList"
          :infinite-scroll-disabled="isOver"
          :infinite-scroll-immediate="false"
        >
          <div
            class="item"
            :class="{
              active: index == songIndex,
            }"
            v-for="(item, index) in list"
          >
            <template v-if="item.status === AiImageStatusEnum.SUCCESS">
              <div class="left">
                <div class="container">
                  <el-image :src="item.imageUrl" fit="cover" />
                  <div class="duration">
                    {{ formatTime(item.duration) }}
                  </div>
                  <button class="play" @click="play(item, index)">
                    <img src="/images/play.svg" data-key="play" alt="" />
                  </button>
                </div>
              </div>
              <div class="center flex-h flex-v flex-between">
                <div>
                  <div class="title">
                    {{ item.title }}
                  </div>
                  <div :title="item.tags" class="tags text-ellipsis">
                    {{ item.tags }}
                  </div>
                </div>
                <div class="mt-5">
                  <el-tooltip effect="light" content="下载歌曲" placement="top">
                    <a :href="item.videoUrl" :download="item.title + '.mp3'" target="_blank">
                      <button class="btn btn-icon t-white">
                        <i class="iconfont icon-xiazai"></i>
                      </button>
                    </a>
                  </el-tooltip>
                  <el-tooltip effect="light" content="删除" placement="top">
                    <button class="btn btn-icon" @click="removeJob(item)">
                      <i class="iconfont icon-shanchu"></i>
                    </button>
                  </el-tooltip>
                </div>
              </div>
            </template>
            <div class="task" v-else>
              <div class="left">
                <div class="title">
                  <span v-if="item.title" :title="item.title">{{ item.title }}</span>
                  <span v-else>{{ item.prompt }}</span>
                </div>
              </div>
              <div class="center h100">
                <div class="failed" v-if="item.status === AiImageStatusEnum.FAIL">
                  {{ item.errorMessage }}
                  <!-- <div>
                    <el-tooltip effect="light" content="删除" placement="top">
                      <button class="btn btn-icon" @click="removeJob(item)">
                        <i class="iconfont icon-remove"></i>
                      </button>
                    </el-tooltip>
                  </div> -->
                </div>
                <Generating v-else />
              </div>
              <!-- <div class="right">
                <el-button type="info" @click="removeJob(item)" circle>
                  <i class="iconfont icon-remove"></i>
                </el-button>
              </div> -->
            </div>
          </div>

          <!-- </div> -->
          <!-- <div class="no-more-data tx-c" v-if="isOver">
            <span>没有更多数据了</span>
            <i class="iconfont icon-face"></i>
          </div> -->
        </div>
        <el-empty :image-size="100" description="没有任何作品，赶紧去创作吧！" v-else />
      </div>

      <!-- <div class="pagination">
        <el-pagination
          v-if="total > pageSize"
          background
          style="
            --el-pagination-button-bg-color: #414141;
            --el-pagination-button-color: #d1d1d1;
            --el-disabled-bg-color: #414141;
            --el-color-primary: #666666;
            --el-pagination-hover-color: #e1e1e1;
          "
          layout="total,prev, pager, next"
          :hide-on-single-page="true"
          v-model:current-page="page"
          v-model:page-size="pageSize"
          @current-change="getMusicList(page)"
          :total="total"
        />
      </div> -->

      <!-- <div class="music-player" v-if="showPlayer">
        <music-player
          :songs="curPlay"
          ref="playerRef"
          :show-close="true"
          @close="showPlayer = false"
        />
      </div> -->
    </div>

    <black-dialog
      v-model:show="showDialog"
      title="修改歌曲"
      @cancal="showDialog = false"
      @confirm="updateSong"
      :width="500"
    >
      <form class="form">
        <div class="form-item">
          <div class="label">歌曲名称</div>
          <input class="input" v-model="editData.title" type="text" />
        </div>

        <div class="form-item">
          <div class="label">封面图片</div>
          <el-upload
            class="avatar-uploader"
            :auto-upload="true"
            :show-file-list="false"
            :http-request="uploadCover"
            accept=".png,.jpg,.jpeg,.bmp"
          >
            <el-avatar :src="editData.cover" shape="square" :size="100" />
          </el-upload>
        </div>
      </form>
    </black-dialog>
  </div>
</template>

<script setup>
import { nextTick, onMounted, onUnmounted, ref, watch, computed, reactive } from "vue";
import { Delete, InfoFilled, UploadFilled, Download } from "@element-plus/icons-vue";
import BlackSelect from "@/components/ui/BlackSelect.vue";
import BlackSwitch from "@/components/ui/BlackSwitch.vue";
import BlackInput from "@/components/ui/BlackInput.vue";
import MusicPlayer from "@/components/MusicPlayer.vue";
import { compact } from "lodash-es";
import { httpGet, httpPost } from "@/utils/http";
import { showMessageError, showMessageOK } from "@/utils/dialog";
import Generating from "@/components/ui/Generating.vue";
import { ElMessage, ElMessageBox } from "element-plus";
import { formatTime } from "@/utils/libs";
import Clipboard from "clipboard";
import BlackDialog from "@/components/ui/BlackDialog.vue";
import Compressor from "compressorjs";
import Empty from "@/components/Empty.vue";
import { MusicApi } from "@/api/ai/music";
import { AiImageStatusEnum } from "@/utils/ai/constants";
import { removeFindIndex } from "@/utils/ai";

const listBoxHeight = ref(0);
const resizeElement = function () {
  listBoxHeight.value = window.innerHeight - 45;
};
const winHeight = ref(window.innerHeight - 50);
const custom = ref(true);
const colWidth = ref(220);
const models = ref([
  { label: "v3.0", value: "chirp-v3-0" },
  { label: "v3.5", value: "chirp-v3-5" },
]);
const tags = ref([
  { label: "女声", value: "female vocals" },
  { label: "男声", value: "male vocals" },
  { label: "流行", value: "pop" },
  { label: "摇滚", value: "rock" },
  { label: "硬摇滚", value: "hard rock" },
  { label: "电音", value: "electronic" },
  { label: "金属", value: "metal" },
  { label: "重金属", value: "heavy metal" },
  { label: "节拍", value: "beat" },
  { label: "弱拍", value: "upbeat" },
  { label: "合成器", value: "synth" },
  { label: "吉他", value: "guitar" },
  { label: "钢琴", value: "piano" },
  { label: "小提琴", value: "violin" },
  { label: "贝斯", value: "bass" },
  { label: "嘻哈", value: "hip hop" },
]);
const data = ref({
  model: "chirp-v3-0",
  tags: "",
  // prompt: "",
  prompt: "",
  title: "",
  makeInstrumental: false,
  ref_task_id: "",
  extend_secs: 0,
  ref_song_id: "",
});
const loading = ref(true);
const isOver = ref(false);
const noData = ref(false);
const isMounted = ref(false);
const curPlay = ref({});
const songIndex = ref(null);
const playerRef = ref(null);
const showPlayer = ref(false);
const list = ref([]);
const btnText = ref("开始创作");
const refSong = ref(null);
const showDialog = ref(false);
const editData = ref({ title: "", cover: "", id: 0 });

const socket = ref(null);
const userId = ref(0);

const isGenStatus = computed(() => {
  return (!data.value.prompt && !data.value.makeInstrumental) || !data.value.title;
});

const { loading: lyricLoading, run: createLyrics } = useRequest(MusicApi.createLyrics, {
  manual: true,
  onSuccess: (res) => {
    data.value.prompt = res.lyric;
  },
});

const { data: lyricPower, run } = useRequest(MusicApi.lyricsPower);
const clipboard = ref(null);
onMounted(() => {
  isMounted.value = true;
  clipboard.value = new Clipboard(".copy-link");
  clipboard.value.on("success", () => {
    ElMessage.success("复制歌曲链接成功！");
  });

  clipboard.value.on("error", () => {
    ElMessage.error("复制失败！");
  });
  initData();
});

// 初始化数据
const initData = async () => {
  await getMusicList();
  // 自动刷新 image 列表
  inProgressTimer.value = setInterval(async () => {
    await refreshWatchImages();
  }, 1000 * 3);
};
// 图片分页相关的参数
const queryParams = reactive({
  pageNo: 0,
  pageSize: 10,
});
// 图片轮询相关的参数（正在生成中的）
const inProgressImageMap = ref({}); // 监听的 image 映射，一般是生成中（需要轮询），key 为 image 编号，value 为 image
const inProgressTimer = ref(); // 生成中的 image 定时器，轮询生成进展
const pageTotal = ref();
/** 获得 image 图片列表 */
const getMusicList = async () => {
  console.log("加载更多");
  if (queryParams.pageNo == 0) {
    isOver.value = false;
  }
  if (isOver.value) {
    return;
  }
  loading.value = true;
  queryParams.pageNo += 1;
  try {
    // 1. 加载图片列表
    // imageListLoadingInstance.value = ElLoading.service({
    //   target: imageListRef.value,
    //   text: "加载中...",
    // });
    const { list: musicList, total } = await MusicApi.getMusicPage(queryParams);
    if (musicList.length < queryParams.pageSize) {
      isOver.value = true;
    }
    if (queryParams.pageNo === 1) {
      list.value = musicList;
    } else {
      list.value = list.value.concat(musicList);
    }
    pageTotal.value = total;

    // 2. 计算需要轮询的图片
    const newWatImages = {};
    list.value.forEach((item) => {
      if (item.status === AiImageStatusEnum.IN_PROGRESS) {
        newWatImages[item.id] = item;
      }
    });
    inProgressImageMap.value = newWatImages;
  } finally {
    // 关闭正在“加载中”的 Loading
    // if (imageListLoadingInstance.value) {
    //   imageListLoadingInstance.value.close();
    //   imageListLoadingInstance.value = null;
    // }
  }
};
/** 轮询生成中的 image 列表 */
const refreshWatchImages = async () => {
  const musicIds = Object.keys(inProgressImageMap.value).map(Number);
  musciLoading.value = true;
  if (musicIds.length == 0) {
    musciLoading.value = false;
    console.log(
      "%c当前无任务",
      "color: white; background-color: #61ee98; padding: 2px 5px; border-radius: 2px"
    );
    return;
  }
  const musicList = await MusicApi.refreshMusicStatus(musicIds);

  const newWatchImages = {};
  musicList.forEach((image) => {
    if (image.status === AiImageStatusEnum.IN_PROGRESS) {
      newWatchImages[image.id] = image;
    } else {
      const index = list.value.findIndex((oldImage) => image.id === oldImage.id);
      if (index >= 0) {
        // 更新 list
        list.value[index] = image;
      }
    }
  });
  let isOver = musicList.every((d) => d.status == AiImageStatusEnum.SUCCESS);
  console.log("isOver :>> ", isOver);
  // if (isOver && !Object.keys(imgGallery.value).length) {
  //   imgGallery.value = list.value[0];
  // }
  inProgressImageMap.value = newWatchImages;
};

onUnmounted(() => {
  clearInterval(inProgressTimer.value);
  clipboard.value.destroy();
  if (socket.value !== null) {
    socket.value.close();
    socket.value = null;
  }
});

const page = ref(0);
const pageSize = ref(10);
const total = ref(0);
const musciLoading = ref(true);
// 创建新的歌曲
const create = () => {
  data.value.type = custom.value ? 2 : 1;
  // data.value.ref_task_id = refSong.value ? refSong.value.task_id : "";
  // data.value.ref_song_id = refSong.value ? refSong.value.song_id : "";
  // data.value.extend_secs = refSong.value ? refSong.value.extend_secs : 0;
  if (custom.value) {
    if (data.value.prompt === "" && !data.value.makeInstrumental) {
      return showMessageError("请输入歌词");
    }
    if (data.value.title === "") {
      return showMessageError("请输入歌曲标题");
    }
  } else {
    if (data.value.prompt === "") {
      return showMessageError("请输入歌曲描述");
    }
  }
  if (refSong.value && data.value.extend_secs > refSong.value.duration) {
    return showMessageError("续写开始时间不能超过原歌曲长度");
  }
  musciLoading.value = true;

  MusicApi.createMusic(data.value)
    .then(async () => {
      if (songIndex.value != null) {
        songIndex.value++;
      }
      queryParams.pageNo = 0;
      await getMusicList();
      showMessageOK("创建任务成功");
    })
    .finally((e) => {
      // musciLoading.value = false;
    });
};

// 续写歌曲
const extend = (item) => {
  refSong.value = item;
  refSong.value.extend_secs = item.duration;
  data.value.title = item.title;
  custom.value = true;
  btnText.value = "续写歌曲";
};

// 更细歌曲
const update = (item) => {
  showDialog.value = true;
  editData.value.title = item.title;
  editData.value.cover = item.cover_url;
  editData.value.id = item.id;
};

const updateSong = () => {
  if (editData.value.title === "" || editData.value.cover === "") {
    return showMessageError("歌曲标题和封面不能为空");
  }
  httpPost("/api/suno/update", editData.value)
    .then(() => {
      showMessageOK("更新歌曲成功");
      showDialog.value = false;
      getMusicList();
    })
    .catch((e) => {
      showMessageError("更新歌曲失败：" + e.message);
    });
};

watch(
  () => custom.value,
  (newValue) => {
    if (!newValue) {
      removeRefSong();
    }
  }
);

const removeRefSong = () => {
  refSong.value = null;
  btnText.value = "开始创作";
};

const play = (item, i) => {
  if (item.status != AiImageStatusEnum.SUCCESS) {
    return;
  }
  curPlay.value = item;
  songIndex.value = i;
  showPlayer.value = true;
  nextTick(() => playerRef.value.play());
};

const selectTag = (tag) => {
  if (data.value.tags.length + tag.value.length >= 119) {
    return;
  }
  data.value.tags = compact([...data.value.tags.split(","), tag.value]).join(",");
};

const removeJob = (item) => {
  ElMessageBox.confirm("此操作将会删除任务相关文件，继续操作吗?", "删除提示", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  })
    .then(() => {
      MusicApi.deleteMusic(item.id)
        .then(() => {
          removeFindIndex(list, item.id, curPlay, songIndex);
          // if (curPlay.value && item.id == curPlay.value.id) {
          //   console.log(
          //     "%c清空当前播放",
          //     "color: white; background-color: #f1738e; padding: 2px 5px; border-radius: 2px"
          //   );
          //   showPlayer.value = false;
          //   curPlay.value = null;
          // }
          ElMessage.success("任务删除成功");
          queryParams.pageNo = 0;
          getMusicList(1);
        })
        .catch((e) => {
          ElMessage.error("任务删除失败：" + e.message);
        });
    })
    .catch(() => {});
};

const uploadCover = (file) => {
  // 压缩图片并上传
  new Compressor(file.file, {
    quality: 0.6,
    success(result) {
      const formData = new FormData();
      formData.append("file", result, result.name);
      // 执行上传操作
      httpPost("/api/upload", formData)
        .then((res) => {
          editData.value.cover = res.data.url;
          ElMessage.success({ message: "上传成功", duration: 500 });
        })
        .catch((e) => {
          ElMessage.error("图片上传失败:" + e.message);
        });
    },
    error(err) {
      console.log(err.message);
    },
  });
};
</script>

<style lang="stylus" scoped>
@import "../assets/css/suno.styl"
</style>
